<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body></body>
  <script>
    // n 元线性方程组
    // factorMatrix 是系数矩阵，仅限于方阵
    // solutionArray 是解列向量
    function resolveLinearFunction(factorMatrix, solutionArray) {
      const size = matrixSize(factorMatrix);
      const sizeArr = matrixSize(solutionArray);
      if (size.row === 0 || sizeArr.column === 0) {
        throw new Error("系数矩阵不能为空，至少要 1 阶；且解向量不能为空！");
      }
      if (sizeArr.column !== 1) {
        throw new Error("解向量只能为 1 列！");
      }
      if (sizeArr.row !== size.row) {
        throw new Error("解向量和系数矩阵的行维度必须相同！");
      }
      let detNum = det(factorMatrix); // 顺带判断方阵
      if (det(factorMatrix) === 0) {
        throw new Error("系数矩阵行列式必须不为 0，否则该方程组有无穷解！");
      }
      const fulaFactorMat = formatMatrix(factorMatrix);
      const fulaSoluArr = formatMatrix(solutionArray);
      if (sizeArr.row === 1) {
        // 一元一次方程组
        return [fulaSoluArr[0] / fulaFactorMat[0]];
      }
      // n 元一次方程组
      let factor = init0Matrix(size.row, size.column); // 存储更换过的系数矩阵
      let calcArr = init0Matrix(sizeArr.row, sizeArr.column); // 存储解向量
      for (let i = 0; i < size.row; i++) {
        let transMatrix = matrixTranspose(fulaFactorMat); // 系数矩阵的转置，等会再重新转置回来
        transMatrix[i] = fulaSoluArr.flat(Infinity);
        // 将解向量替换原向量的列向量，应用了克拉默法则
        calcArr[i] = det(matrixTranspose(transMatrix)) / detNum;
      }
      return calcArr;
    }

    // 展示两个数相除之后，如果不是整数，就展示分数而不是小数
    // num1 / num2
    function displayFraction(num1, num2) {
      if (!num2) {
        throw new Error("除数（第二个参数）不能为空！");
      }
      if (num1.constructor !== Number || num2.constructor !== Number) {
        throw new Error("输入参数必须是数字形式！");
      }
      const calc = num1 / num2;
      if (!Number.isFinite(calc) || Number.isNaN(calc)) {
        throw new Error("输入的数字必须是有限实数！");
      }
      if (Number.isInteger(calc)) {
        return calc;
      } else {
        let realNum1, realNum2;
        // 如果输入的是小数的情况
        let i = 0;
        do {
          realNum1 = Math.abs(num1) * Math.pow(10, i);
          realNum2 = Math.abs(num2) * Math.pow(10, i);
          i++;
        } while (
          !(
            Number.isInteger(realNum1 - ~~realNum1) &&
            Number.isInteger(realNum2 - ~~realNum2)
          )
        );
        let mins = Math.min(realNum1, realNum2);
        let calc1 = realNum1;
        let calc2 = realNum2;
        for (let i = 0; i < mins + 1; i++) {
          if (realNum1 % i === 0 && realNum2 % i === 0) {
            realNum1 = realNum1 / i;
            realNum2 = realNum2 / i;
            i = 1; // 重新开始寻找因子
          }
        }
        return `${calc > 0 ? "" : "-"}${realNum1} / ${realNum2}`;
      }
    }

    // 小数转化为假分数
    function fraAndDecTransfer(num) {
      if (num === 0) {
        return num;
      }
      if (num.constructor !== Number || Number.isNaN(num)) {
        throw new Error("输入的数字必须是数字形式！");
      }
      if (!Number.isFinite(num)) {
        throw new Error("输入的数字必须是有限实数！");
      }
      if (Number.isInteger(num)) {
        return num;
      }
      // 输入的是普通带小数点的数字
      const floorNum = Math.abs(~~num); // 存储小数的正数部分
      const pointStr = num.toString().split(".")[1]; // 间接存储小数部分以及其位数，也是小数部分分子
      if (pointStr.length > 10) {
        throw new Error(
          "输入数字的小数位最好不要大于 10 位，否则程序运行过慢！"
        );
      }
      // 开始处理 0 ~ 1 之间的小数
      const denominator = Math.pow(10, pointStr.length); // 分母
      let flagPoint = pointStr - 0 + floorNum * denominator; // 存储约分后的分子结果
      let flagDeno = denominator; // 存储约分后的分母结果
      for (let i = 2; i < ~~(Math.min(flagPoint, flagDeno) / 3) + 10; i++) {
        // 除以 3 是为了减少循环次数，让可以判断的小数位更多一些
        if (pointStr % i === 0 && denominator % i === 0) {
          flagPoint = (pointStr - 0 + floorNum * denominator) / i;
          flagDeno = denominator / i;
        }
      }
      return `${num < 0 ? "-" : ""}${flagPoint} / ${flagDeno}`;
    }

    // 分数初等运算（加，乘）
    // calcMode 计算模式：add 加法；multi 乘法
    /*
      分数的标准模式必须是：
        "-a / b"    负 b 分之a
        "q / c"     c 分之 q
    */
    function fractionElementaryCalculate(frac1, frac2, calcMode) {
      const fulaNum1 =
        frac1.constructor === Number ? fraAndDecTransfer(frac1) : frac1; // 标准化
      const fulaNum2 =
        frac2.constructor === Number ? fraAndDecTransfer(frac2) : frac2; // 标准化

      if (calcMode === "add") {
        // 加法
        /* 1 两者都是数字形式 */
        if (
          fulaNum1.constructor === Number &&
          fulaNum2.constructor === Number
        ) {
          return fulaNum1 + fulaNum2;
        }
        /* 2 前者是数字，后者是字符串（分数模式） */
        if (
          fulaNum1.constructor === Number &&
          fulaNum2.constructor === String
        ) {
          let fNum2Point = fulaNum2.split(" / ")[0] * 1; // 原始分子
          let fNum2Deno = fulaNum2.split(" / ")[1] * 1; // 原始分母
          fNum2Point += fulaNum1 * fNum2Deno;
          return displayFraction(fNum2Point, fNum2Deno);
        }
        /* 3 后者是数字，前者是字符串（分数模式） */
        if (
          fulaNum1.constructor === String &&
          fulaNum2.constructor === Number
        ) {
          let fNum1Point = fulaNum1.split(" / ")[0] * 1; // 原始分子
          let fNum1Deno = fulaNum1.split(" / ")[1] * 1; // 原始分母
          fNum1Point += fulaNum2 * fNum1Deno;
          return displayFraction(fNum1Point, fNum1Deno);
        }
        /* 4 前者、后者都是字符串（分数模式） */
        if (
          fulaNum1.constructor === String &&
          fulaNum2.constructor === String
        ) {
          let fNum1Point = fulaNum1.split(" / ")[0] * 1; // 原始分子
          let fNum1Deno = fulaNum1.split(" / ")[1] * 1; // 原始分母
          let fNum2Point = fulaNum2.split(" / ")[0] * 1; // 原始分子
          let fNum2Deno = fulaNum2.split(" / ")[1] * 1; // 原始分母

          return displayFraction(
            fNum1Point * fNum2Deno + fNum2Point * fNum1Deno,
            fNum1Deno * fNum2Deno
          );
        }
        throw new Error("格式不正确！");
      } else if (calcMode === "multi") {
        // 乘法
        /* 1 两者都是数字形式 */
        if (
          fulaNum1.constructor === Number &&
          fulaNum2.constructor === Number
        ) {
          return fulaNum1 * fulaNum2;
        }
        /* 2 前者是数字，后者是字符串（分数模式） */
        if (
          fulaNum1.constructor === Number &&
          fulaNum2.constructor === String
        ) {
          let fNum2Point = fulaNum2.split(" / ")[0] * 1; // 原始分子
          let fNum2Deno = fulaNum2.split(" / ")[1] * 1; // 原始分母
          fNum2Point = fulaNum1 * fNum2Point;
          return displayFraction(fNum2Point, fNum2Deno);
        }
        /* 3 后者是数字，前者是字符串（分数模式） */
        if (
          fulaNum1.constructor === String &&
          fulaNum2.constructor === Number
        ) {
          let fNum1Point = fulaNum1.split(" / ")[0] * 1; // 原始分子
          let fNum1Deno = fulaNum1.split(" / ")[1] * 1; // 原始分母
          fNum1Point = fulaNum2 * fNum1Point;
          return displayFraction(fNum1Point, fNum1Deno);
        }
        /* 4 前者、后者都是字符串（分数模式） */
        if (
          fulaNum1.constructor === String &&
          fulaNum2.constructor === String
        ) {
          let fNum1Point = fulaNum1.split(" / ")[0] * 1; // 原始分子
          let fNum1Deno = fulaNum1.split(" / ")[1] * 1; // 原始分母
          let fNum2Point = fulaNum2.split(" / ")[0] * 1; // 原始分子
          let fNum2Deno = fulaNum2.split(" / ")[1] * 1; // 原始分母

          return displayFraction(
            fNum1Point * fNum2Point,
            fNum1Deno * fNum2Deno
          );
        }
        throw new Error("格式不正确！");
      } else {
        throw new Error("第三个参数（计算模式）只有两种参数 add 和 multi ！");
      }
    }

    let num0 = 0.67;
    let num1 = 0.98;
    let inta = 3;
    let numstr = "-98 / 100";
    console.log(displayFraction(16500, 10000));

    const pointGroup = {
      line1: [
        {
          x: 10,
          y: 10,
        },
        {
          x: 100,
          y: 100,
        },
      ],
      line2: [
        {
          x: -10,
          y: 10,
        },
        {
          x: 20,
          y: 0,
        },
      ],
    };


  </script>
</html>
